using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Caching;
using System.Data.SqlClient;
using System.Data;
using System.IO;
using System.ComponentModel;
using System.Web.Security;

[assembly: TagPrefix("BeeldBank.Toolkit", "bb")]

namespace BeeldBank.Toolkit
{
    [ToolboxData("<{0}:SqlImage Runat=\"server\">")]
    public class SqlImage : System.Web.UI.WebControls.Image
    {
        [
            Browsable(true), Category("Database"), DefaultValue(""),
            Description("Database connection string")
        ]
        public string ConnectionString
        {
            get
            {
                object o = ViewState["ConnectionString"];
                return (o == null) ? String.Empty : (string)o;
            }
            set { ViewState["ConnectionString"] = value; }
        }

        [
            Browsable(true), Category("Database"), DefaultValue(""),
            Description("Database SELECT command")
        ]
        public string SelectCommand
        {
            get
            {
                object o = ViewState["SelectCommand"];
                return (o == null) ? String.Empty : (string)o;
            }
            set { ViewState["SelectCommand"] = value; }
        }

        [
            Browsable(true), DefaultValue("image/jpeg"),
            Description("MIME type (e.g., image/jpeg)")
        ]
        public string MimeType
        {
            get
            {
                object o = ViewState["MimeType"];
                return (o == null) ? "image/jpeg" : (string)o;
            }
            set { ViewState["MimeType"] = value; }
        }

        [
            Browsable(true), Category("Caching"), DefaultValue("false"),
            Description("Specifies whether image should be cached in memory")
        ]
        public bool EnableCaching
        {
            get
            {
                object o = ViewState["EnableCaching"];
                return (o == null) ? false : (bool)o;
            }
            set { ViewState["EnableCaching"] = value; }
        }

        [
            Browsable(true), Category("Caching"), DefaultValue(""),
            Description("Cache duration in seconds")
        ]
        public int CacheDuration
        {
            get
            {
                object o = ViewState["CacheDuration"];
                return (o == null) ? 0 : (int)o;
            }
            set { ViewState["CacheDuration"] = value; }
        }

        protected override void OnPreRender(EventArgs e)
        {
            if (!String.IsNullOrEmpty(ConnectionString) && !String.IsNullOrEmpty(SelectCommand))
            {
                ImageUrl = String.Format("~/__ImageGrabber.axd?n={0}&s={1}&m={2}&c={3}&d={4}",
                    EncryptionHelper.Encrypt(ConnectionString), EncryptionHelper.Encrypt(SelectCommand),
                    MimeType, EnableCaching ? "1" : "0", CacheDuration);
            }
        }
    }

    public class ImageGrabber : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            // Read the query string parameters
            string connectionString = context.Request["n"];
            if (String.IsNullOrEmpty(connectionString))
                return;

            string selectCommand = context.Request["s"];
            if (String.IsNullOrEmpty(selectCommand))
                return;

            string mimeType = context.Request["m"];
            if (String.IsNullOrEmpty(mimeType))
                mimeType = "image/jpeg";

            bool enableCaching = false;
            string caching = context.Request["c"];
            if (!String.IsNullOrEmpty(caching) && caching == "1")
                enableCaching = true;

            int cacheDuration = 0;
            string duration = context.Request["d"];
            if (!String.IsNullOrEmpty(duration))
                cacheDuration = Convert.ToInt32(duration);

            byte[] image = null;
            string cacheKey = String.Format("__ImageGrabber_{0}_{1}", connectionString, selectCommand);

            // Attempt to read the image from the cache if caching is enabled
            if (enableCaching)
                image = (byte[])context.Cache[cacheKey];

            // If the image isn't cached or caching isn't enabled, query the database
            if (image == null)
            {
                using (SqlConnection connection = new SqlConnection(EncryptionHelper.Decrypt(connectionString)))
                {
                    using (MemoryStream stream = new MemoryStream())
                    {
                        connection.Open();
                        SqlCommand command = new SqlCommand(EncryptionHelper.Decrypt(selectCommand), connection);
                        try
                        {
                            image = (byte[])command.ExecuteScalar();
                            if (enableCaching)
                                context.Cache.Insert(cacheKey, image, null, DateTime.Now.AddSeconds(cacheDuration), Cache.NoSlidingExpiration);
                        }
                        catch (Exception)
                        {
                        }
                    }
                }
            }

            // Stream the image bits back to the browser
            try
            {
                context.Response.ContentType = mimeType;
                context.Response.OutputStream.Write(image, 0, image.Length);
            }
            catch (Exception)
            {
            }
        }

        public bool IsReusable
        {
            get { return true; }
        }
    }

    class EncryptionHelper
    {
        // This isn't very elegant, but it's effective. Place data
        // to be encrypted in a FormsAuthenticationTicket's UserData
        // property and encrypt the ticket. To decrypt, decrypt the
        // encrypted ticket and extract the UserData property. Use
        // FormsAuthentication.Encrypt and FormsAuthentication.Decrypt
        // as work-arounds for ASP.NET's lack of a public API for
        // encrypting and decrypting data.

        public static string Encrypt(string input)
        {
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, String.Empty, DateTime.Now, DateTime.MaxValue, false, input);
            return FormsAuthentication.Encrypt(ticket);
        }

        public static string Decrypt(string input)
        {
            FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(input);
            return ticket.UserData;
        }
    }
}